// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef QUICHE_QUIC_CORE_QUIC_CONNECTION_ID_H_
#define QUICHE_QUIC_CORE_QUIC_CONNECTION_ID_H_

#include <cstdint>
#include <string>

#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "quiche/common/platform/api/quiche_export.h"

namespace quic {

// This is a property of QUIC headers, it indicates whether the connection ID
// should actually be sent over the wire (or was sent on received packets).
enum QuicConnectionIdIncluded : uint8_t {
  CONNECTION_ID_PRESENT = 1,
  CONNECTION_ID_ABSENT = 2,
};

// Maximum connection ID length supported by versions that use the encoding from
// draft-ietf-quic-invariants-06.
inline constexpr uint8_t kQuicMaxConnectionIdWithLengthPrefixLength = 20;

// Maximum connection ID length supported by versions that use the encoding from
// draft-ietf-quic-invariants-05.
inline constexpr uint8_t kQuicMaxConnectionId4BitLength = 18;

// kQuicDefaultConnectionIdLength is the only supported length for QUIC
// versions < v99, and is the default picked for all versions.
inline constexpr uint8_t kQuicDefaultConnectionIdLength = 8;

// According to the IETF spec, the initial server connection ID generated by
// the client must be at least this long.
inline constexpr uint8_t kQuicMinimumInitialConnectionIdLength = 8;

class QUICHE_EXPORT QuicConnectionId {
 public:
  // Creates a connection ID of length zero.
  QuicConnectionId();

  // Creates a connection ID from network order bytes.
  QuicConnectionId(const char* data, uint8_t length);
  QuicConnectionId(absl::Span<const uint8_t> data);
  QuicConnectionId(absl::string_view data);

  // Creates a connection ID from another connection ID.
  QuicConnectionId(const QuicConnectionId& other);

  // Assignment operator.
  QuicConnectionId& operator=(const QuicConnectionId& other);

  ~QuicConnectionId();

  // Returns the length of the connection ID, in bytes.
  uint8_t length() const;

  // Sets the length of the connection ID, in bytes.
  // WARNING: Calling set_length() can change the in-memory location of the
  // connection ID. Callers must therefore ensure they call data() or
  // mutable_data() after they call set_length().
  void set_length(uint8_t length);

  // Returns a pointer to the connection ID bytes, in network byte order.
  const char* data() const;

  // Returns a mutable pointer to the connection ID bytes,
  // in network byte order.
  char* mutable_data();

  // Returns whether the connection ID has length zero.
  bool IsEmpty() const;

  // Hash() is required to use connection IDs as keys in hash tables.
  // During the lifetime of a process, the output of Hash() is guaranteed to be
  // the same for connection IDs that are equal to one another. Note however
  // that this property is not guaranteed across process lifetimes. This makes
  // Hash() suitable for data structures such as hash tables but not for sending
  // a hash over the network.
  size_t Hash() const;

  // Allow absl::Hash to hash std::pair<QuicConnectionId, H>.
  template <typename H>
  friend H AbslHashValue(H h, const QuicConnectionId& c) {
    return H::combine(std::move(h), c.Hash());
  }

  // Generates an ASCII string that represents
  // the contents of the connection ID in hex, or "0" if it is empty.
  std::string ToString() const;
  // ToStringView() is not in hex. Returns "" if empty.
  absl::string_view ToStringView() const {
    return absl::string_view(data(), length());
  }

  // operator<< allows easily logging connection IDs.
  friend QUICHE_EXPORT std::ostream& operator<<(std::ostream& os,
                                                const QuicConnectionId& v);

  bool operator==(const QuicConnectionId& v) const;
  bool operator!=(const QuicConnectionId& v) const;
  // operator< is required to use connection IDs as keys in hash tables.
  bool operator<(const QuicConnectionId& v) const;

 private:
  // The connection ID is represented in network byte order.
  union {
    // If the connection ID fits in |data_short_|, it is stored in the
    // first |length_| bytes of |data_short_|.
    // Otherwise it is stored in |data_long_| which is guaranteed to have a size
    // equal to |length_|.
    // A value of 23 was chosen because it can hold 20 byte IDs that are legal
    // in currently supported versions, expanded to a 24-byte struct to be 8-
    // byte aligned. Unknown versions that would trigger version negotiation
    // packets might have IDs beyond 23 bytes, which are stored in |data_long_|.
    struct {
      uint8_t padding_;  // Match length_ field of the other union member.
      char data_short_[23];
    };
    struct {
      uint8_t length_;  // length of the connection ID, in bytes.
      char* data_long_;
    };
  };
};

// Creates a connection ID of length zero, unless the restart flag
// quic_connection_ids_network_byte_order is false in which case
// it returns an 8-byte all-zeroes connection ID.
QUICHE_EXPORT QuicConnectionId EmptyQuicConnectionId();

// QuicConnectionIdHash can be passed as hash argument to hash tables.
// During the lifetime of a process, the output of QuicConnectionIdHash is
// guaranteed to be the same for connection IDs that are equal to one another.
// Note however that this property is not guaranteed across process lifetimes.
// This makes QuicConnectionIdHash suitable for data structures such as hash
// tables but not for sending a hash over the network.
class QUICHE_EXPORT QuicConnectionIdHash {
 public:
  size_t operator()(QuicConnectionId const& connection_id) const noexcept {
    return connection_id.Hash();
  }
};

}  // namespace quic

#endif  // QUICHE_QUIC_CORE_QUIC_CONNECTION_ID_H_
